package com.yby6.web.controller.other;


import com.google.gson.Gson;
import com.yby6.common.constant.Constants;
import com.yby6.common.core.domain.SysUser;
import com.yby6.common.utils.ServletUtils;
import com.yby6.common.utils.http.HttpsUtils;
import com.yby6.system.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import com.yby6.common.core.AjaxResult;
import com.yby6.common.utils.BCryptUtil;
import com.yby6.framework.manager.AsyncManager;
import com.yby6.framework.manager.factory.AsyncFactory;
import com.yby6.framework.web.service.SysLoginService;
import com.yby6.system.vo.QQ.OAuthProperties;
import com.yby6.system.vo.QQ.vo.QQDTO;
import com.yby6.system.vo.QQ.vo.QQOpenidDTO;
import com.yby6.system.vo.SysUserVo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

// Create By Yang Shuai,Date 2022/4/18:13:00
@Slf4j
@Controller
@RequestMapping("/system/tencent")
@RequiredArgsConstructor
public class TencentController {

    private final OAuthProperties oauth;

    private final SysUserService sysUserService;

    private final SysLoginService sysLoginService;


    // 记录当前第三方登录是否登录成功
    private volatile boolean isLogin;
    // 记录当前是否登录失败
    private volatile boolean isLoginFail;
    // 记录当前用户的token（本系统saToken生成并且保存到redis当中）
    private volatile String token;

    /**
     * 调用QQ登录链接拿到code并且进行回调到本项目地址
     *
     * @return
     * @throws Exception
     */
    @GetMapping("code")
    public ResponseEntity<Object> getCode() {
        String url = oauth.getQQ().getCode_callback_uri() + //获取code码地址
                "?client_id=" + oauth.getQQ().getClient_id()//appid
                + "&state=" + UUID.randomUUID() + //这个说是防攻击的，就给个随机uuid吧
                "&redirect_uri=" + oauth.getQQ().getRedirect_uri() +//这个很重要，这个是回调地址，即就收腾讯返回的code码
                "&response_type=code";
        return ResponseEntity.ok(url);
    }

    /**
     * 接收回调地址带过来的code码
     */
    @GetMapping("/oauth2")
    public void authorizeQQ(String code, HttpServletResponse response) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        params.put("redirect_uri", oauth.getQQ().getRedirect_uri());
        params.put("client_id", oauth.getQQ().getClient_id());
        params.put("client_secret", oauth.getQQ().getClient_secret());

        // 获取腾讯access token
        Map<String, String> reulsts = getAccessToken(params);

        // 到这里access_token已经处理好了
        // 下一步获取openid，只有拿到openid才能拿到用户信息
        String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + reulsts.get("access_token"));
        // callback( {"client_id":"101887062","openid":"74DD1353321FD56375F34422D833848D"} );
        // System.out.println("openidContent: " + openidContent);

        //接下来对openid进行处理
        //截取需要的那部分json字符串
        String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
        // json 转 对象
        Gson gson = new Gson();
        //将返回的openid转换成DTO
        QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);
        // 封装参数 请求用户信息数据
        params.clear();
        //设置access_token
        params.put("access_token", reulsts.get("access_token"));
        //设置openid
        params.put("openid", qqOpenidDTO.getOpenid());
        //设置appid
        params.put("oauth_consumer_key", qqOpenidDTO.getClient_id());
        //获取用户信息
        String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), params);
        QQDTO qqDTO = gson.fromJson(userInfo, QQDTO.class);
        // （正常情况下，在开发时候用openid作为用户名，再自己定义个密码就可以了）
        try {
            // 开始鉴权
            SysUserVo sysUserVo = buildUserClass(qqDTO, qqOpenidDTO.getOpenid());
            Serializable login = getSerializable(qqOpenidDTO.getOpenid(), sysUserVo);
            this.token = String.valueOf(login);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("第三方登录错误：{}", e.getCause().getLocalizedMessage());
        }
    }

    /**
     * 调用登陆操作
     */
    private Serializable getSerializable(String qqOpenidDTO, SysUserVo sysUserVo) {
        // 1.判断是否存在该用户
        SysUser userByOpenId = this.sysUserService.getUserByOpenId(qqOpenidDTO);
        if (userByOpenId != null && userByOpenId.getId() != null) {
            this.sysUserService.updateByOpenId(sysUserVo);
        } else {
            this.sysUserService.insert(sysUserVo);
        }
        SysUser sysUser = SysUser.builder().username(sysUserVo.getUsername()).password(sysUserVo.getPassword()).type(Constants.ONE).build();
        Serializable login = sysLoginService.login(sysUser, ServletUtils.getRequest());
        return login;
    }


    /**
     * 检查是否QQ登录成功
     *
     * @return 结果
     */
    @GetMapping("/checkLogin")
    @ResponseBody
    public AjaxResult checkLogin() {
        AjaxResult ajaxResult = null;
        int flag = 1;
        while (true) {
            log.info("开始检查是否登录成功:{}", this.isLogin);
            if (this.isLogin) {
                ajaxResult = AjaxResult.success().addParam("token", this.token);
                // 状态复原
                this.isLogin = false;
                break;
            }
            // 登录失败
            if (this.isLoginFail) {
                ajaxResult = AjaxResult.error("第三方登录失败");
                break;
            }
            // 500毫秒查询一下状态
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 超时登录处理 1分钟处理
            if (flag > 50) {
                ajaxResult = AjaxResult.error("用户登录超时请重新登录！");
            }
        }
        return ajaxResult;
    }

    /**
     * 获取用户信息(获取本系统token)
     * <p>
     * 思路： 根据前端拿到的openid and access_token 来去qq里面拿用户信息
     *
     * @return AjaxResult
     */
    @PostMapping("/getUserInfo")
    @ResponseBody
    public AjaxResult getUserInfo(@RequestBody Map<String, Object> map, HttpServletRequest request) {
        String accessToken = map.get("access_token").toString();
        String openid = map.get("openid").toString();
        String oauthConsumerKey = map.get("oauth_consumer_key").toString();
        //获取用户信息
        String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), map);
        QQDTO qqDTO = new Gson().fromJson(userInfo, QQDTO.class);

        // 开始鉴权
        SysUserVo sysUserVo = buildUserClass(qqDTO, openid);
        // 1.判断是否存在该用户
        Serializable login = getSerializable(openid, sysUserVo);
        this.token = String.valueOf(login);
        // 异步工厂实现
        final SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(sysUserVo, sysUser);
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser, "QQ登录", false, request));
        return AjaxResult.success().addParam("token", this.token);
    }

    /**
     * 根据QQ登录提供的access_token获取openId
     *
     * @param map
     * @return openId
     */
    @PostMapping("/getOpenIdByAccessToken")
    @ResponseBody
    public AjaxResult getOpenIdByAccessToken(@RequestBody Map<String, Object> map) {
        //下一步获取openid，只有拿到openid才能拿到用户信息
        String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + map.get("access_token"));
        //接下来对openid进行处理
        String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
        //将返回的openid转换成DTO
        QQOpenidDTO qqOpenidDTO = new Gson().fromJson(openid, QQOpenidDTO.class);
        return AjaxResult.success().addParam("openid", qqOpenidDTO.getOpenid());
    }


    /**
     * 构造QQ登录实体类
     *
     * @param qqDTO  第三方登录返回的参数
     * @param openid 第三方登录的ID
     * @return 实体结果
     */
    private SysUserVo buildUserClass(QQDTO qqDTO, String openid) {
        SysUserVo user = new SysUserVo();
        user.setNickName(qqDTO.getNickname());
        user.setUsername(qqDTO.getNickname());
        user.setHeader(qqDTO.getFigureurl_2());
        user.setPassword(BCryptUtil.createPassword("123456" + openid));
        user.setNote("QQ登陆-" + qqDTO.getNickname());
        user.setOpenId(openid);
        Long[] roleIds = new Long[]{11L};
        user.setRoleIds(roleIds);
        user.setStatus(1);
        user.setType(1);
        return user;
    }


    /**
     * 获取腾讯 access_token
     */
    public Map<String, String> getAccessToken(HashMap<String, Object> params) {
        // 认证地址
        //获取access_token如：access_token=9724892714FDF1E3ED5A4C6D074AF9CB&expires_in=7776000&refresh_token=9E0DE422742ACCAB629A54B3BFEC61FF
        String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params);
        //对拿到的数据进行切割字符串
        String[] strings = result.split("&");
        //切割好后放进map
        Map<String, String> reulsts = new HashMap<>();
        for (String str : strings) {
            String[] split = str.split("=");
            if (split.length > 1) {
                reulsts.put(split[0], split[1]);
            }
        }
        return reulsts;
    }

}
